<?php

namespace App\Library;

use App\Constants\ErrorCode;
use App\Exception\BusinessException;
use Hyperf\HttpServer\Contract\RequestInterface;
use Yansongda\Pay\Exceptions\GatewayException;
use Yansongda\Pay\Exceptions\InvalidArgumentException;
use Yansongda\Pay\Exceptions\InvalidConfigException;
use Yansongda\Pay\Exceptions\InvalidSignException;
use Yansongda\Pay\Gateways\Alipay;
use Yansongda\Pay\Gateways\Wechat;
use Yansongda\Pay\Log;
use Yansongda\Pay\Pay;
use Yansongda\Supports\Collection;
use Hyperf\Di\Annotation\Inject;

/**
 * 支付管理库
 * Class Paybase
 * @package App\Library
 */
class Paybase
{

    /**
     * @Inject
     * @var RequestInterface
     */
    protected $request;

    /**
     * 提交订单支付
     * @param array|null $amount
     * @param string|null $orderid
     * @param string|null $type
     * @param string|null $title
     * @param string|null $notifyurl
     * @param string|null $returnurl
     * @param string|null $method
     * @return false|string|Collection
     */
    public function submitOrder(?array $amount, ?string $orderid = null, ?string $type = null, ?string $title = null, ?string $notifyurl = null, ?string $returnurl = null, ?string $method = null)
    {
        if (!is_array($amount)) {
            $params = [
                'amount' => $amount,
                'orderid' => $orderid,
                'type' => $type,
                'title' => $title,
                'notifyurl' => $notifyurl,
                'return_url' => $returnurl,
                'method' => $method
            ];
        } else {
            $params = $amount;
        }
        if (!in_array($params['type'], ['wechat', 'alipay'])) {
            throw new BusinessException(ErrorCode::NORMAL_ERROR, '支付方式不存在');
        }
        $type = isset($params['type']) && in_array($params['type'], ['alipay', 'wechat']) ? $params['type'] : 'wechat';
        $method = isset($params['method']) ? $params['method'] : 'web';
        $orderid = isset($params['orderid']) ? $params['orderid'] : date("YmdHis") . mt_rand(100000, 999999);
        $amount = isset($params['amount']) ? $params['amount'] : 1;
        $title = isset($params['title']) ? $params['title'] : "支付";
        $auth_code = isset($params['auth_code']) ? $params['auth_code'] : '';
        $openid = isset($params['openid']) ? $params['openid'] : '';

        $html = '';
        $config = Paybase::getConfig($type);
        $config['return_url'] = $params['return_url'];
        $pay = Pay::$type($config);
        if ($type == 'alipay') {
            $data = [
                'out_trade_no' => $orderid, // (外部支付单号)
                'total_amount' => $amount,  // 单位元
                'subject' => $title,
            ];

            switch ($method) {
                case 'web':
                    $html = $pay->web($data);
                    return $html->getContent();
                    break;
                case 'wap':
                    $html = $pay->wap($data);
                    return $html->getContent();
                    break;
                case 'mp':
                case 'app':
                    // app支付
                    $html = $pay->app($data);
                    return $html->getContent();
                    break;
                case 'tt':
                    break;
                case 'scan':
                    //扫码支付,直接返回字符串
                    $html = $pay->scan($data);
                    return $html;
                    break;
                case 'pos':
                    $data['auth_code'] = $auth_code;
                    $html = $pay->pos($data);
                    return $html;
                    break;
                default:
            }
        } else {
            $data = [
                'out_trade_no' => $orderid,//你的订单号
                'body' => $title,
                'total_fee' => $amount >= 0.03 ? $amount * 100 : 300,// 单位分
            ];
            switch ($method) {
                case 'wap':
                case 'web':
                    break;
                case 'mp':
                    //公众号支付必须有openid
                    $params['openid'] = $openid;
                    break;
                case 'app':
                    $html = $pay->app($data);
                    return json_decode($html->getContent(), true);
                    break;
                case 'scan':
                    //扫码支付,直接返回字符串
                    $html = $pay->scan($data);
                    return $html;
                    break;
                case 'pos':
                    //刷卡支付,直接返回字符串（必须要有auth_code）
                    $params['auth_code'] = $auth_code;
                    break;
                case 'miniapp':
                    //小程序支付,直接返回字符串（必须要有openid）
                    $data['openid'] = $openid;
                    $html = $pay->miniapp($data);
                    break;
                default:
            }
        }
        //返回字符串
        $html = is_array($html) ? json_encode($html) : $html;
        return $html;
    }

    /**
     * 参数校验
     * @param $type
     * @return bool| Alipay |Wechat
     */
    public function checkNotify($type)
    {
        $type = strtolower($type);
        if (!in_array($type, ['wechat', 'alipay'])) {
            echo '支付方式不存在';
            return false;
        }
        try {
            // 支付配置
            $config = self::getConfig($type);
            $pay = Pay::$type($config);
            $data = $type == 'wechat' ? $this->request->getBody()->getContents() : $this->request->all();
            $data = $pay->verify($data);
            if ($type == 'alipay') {
                if (in_array($data['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED'])) {
                    return $pay;
                }
            } else {
                return $pay;
            }
        } catch (\Throwable $e) {
            print_r($e->getMessage());
            return false;
        }
        return false;
    }

    /**
     * 验证返回是否成功
     * @param string $type 支付类型
     * @param array $config 配置信息
     * @return bool|Pay
     */
    public function checkReturn($type, $config = [])
    {
        $type = strtolower($type);
        if (!in_array($type, ['wechat', 'alipay'])) {
            return false;
        }
        //微信无需验证
        if ($type == 'wechat') {
            return true;
        }
        try {
            $config = self::getConfig($type);
            $pay = Pay::$type($config);
            $data = $type == 'wechat' ? file_get_contents("php://input") : $this->request->get('', null, 'trim');
            $data = $pay->verify($data);
            if ($data) {
                return $pay;
            }
        } catch (\Throwable $e) {
            print_r($e->getMessage());
            return false;
        }
        return false;
    }

    /**
     * 微信退款
     * @param $out_trade_no
     * @param $refund_fee
     * @return mixed
     * @throws GatewayException
     * @throws InvalidArgumentException
     * @throws InvalidSignException
     */
    public function wx_refund($out_trade_no, $refund_fee)
    {

        $config = self::getConfig('wechat');
        $pay = Pay::wechat($config);
        $order = [
            'out_trade_no' => $out_trade_no,
            'out_refund_no' => time(),
            'total_fee' => $refund_fee * 100,
            'refund_fee' => $refund_fee * 100,
            'refund_desc' => '微信退款',
        ];
        return $pay->refund($order);
    }

    /**
     * 支付宝退款
     * @param $out_trade_no
     * @param $refund_fee
     * @return mixed
     * @throws GatewayException
     * @throws InvalidConfigException
     * @throws InvalidSignException
     */
    public function ali_refund($out_trade_no, $refund_fee)
    {
        $config = self::getConfig('alipay');
        $pay = Pay::alipay($config);
        $order = [
            'out_trade_no' => $out_trade_no,
            'refund_amount' => $refund_fee,
        ];
        return $pay->refund($order);
    }


    /**
     * 获取配置
     * @param string $type 支付类型
     * @return array|mixed
     */
    public function getConfig($type = 'wechat'): array
    {
        $config = config('payconfig.' . $type);
        $config = isset($config) ? $config : config('payconfig.' . 'wechat');

        if ($config['log']) {
            $config['log'] = [
                'file' => BASE_PATH . '/epaylogs/' . $type . '-' . date("Y-m-d") . '.log',
                'level' => 'debug'
            ];
        }

        if (isset($config['cert_client']) && substr($config['cert_client'], 0, 6) == '/certs') {
            $config['cert_client'] = BASE_PATH . $config['cert_client'];
        }
        if (isset($config['cert_key']) && substr($config['cert_key'], 0, 6) == '/certs') {
            $config['cert_key'] = BASE_PATH . $config['cert_key'];
        }
        if (isset($config['ali_public_key']) && substr($config['ali_public_key'], 0, 6) == '/certs') {
            $config['ali_public_key'] = BASE_PATH . $config['ali_public_key'];
        }
        if (isset($config['alipay_root_cert']) && substr($config['alipay_root_cert'], 0, 6) == '/certs') {
            $config['alipay_root_cert'] = BASE_PATH . $config['alipay_root_cert'];
        }
        if (isset($config['app_cert_public_key']) && substr($config['app_cert_public_key'], 0, 6) == '/certs') {
            $config['app_cert_public_key'] = BASE_PATH . $config['app_cert_public_key'];
        }
        // 通知地址
        $config['notify_url'] = empty($config['notify_url']) ? 'epay/api/notifyx' . '/type/' . $type : $config['notify_url'];
        $config['notify_url'] = preg_match("/^(http:\/\/|https:\/\/)/i", $config['notify_url']) ? $config['notify_url'] : "https://" . $config['notify_url'];
        // 返回页面
        $config['return_url'] = empty($config['return_url']) ? 'epay/api/returnx' . '/type/' . $type : $config['return_url'];
        $config['return_url'] = preg_match("/^(http:\/\/|https:\/\/)/i", $config['return_url']) ? $config['return_url'] : "https://" . $config['return_url'];
        return $config;
    }

}